package xdb;

import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

import javax.xml.parsers.DocumentBuilderFactory;
import java.io.File;
import java.util.HashMap;
import java.util.Map;

public final class XdbConf {

    private String defaultTableCache;
    private int serialKey;
    private boolean xdbVerify;
    private boolean isJson;
    private int corePoolSize;
    private int procPoolSize;
    private int schedPoolSize;
    private volatile int timeoutPeriod;
    private volatile long snapshotFatalTime;
    private volatile int marshalPeriod;
    private volatile int marshalN;
    private boolean allowCheckpointXXX; // 危险参数，仅用于测试。
    private volatile int checkpointPeriod;
    private int angelPeriod;
    private int localId;
    private ProcedureConf procedureConf;
    private final Map<String, TableConf> tableconf = new HashMap<>();
    private int closeCache;//关闭缓存 1=关闭缓存 0=使用缓存  默认=0
    private volatile boolean autoMarshal = true;
    private volatile File libdbPath = null;
    public XdbConf(String xdbxml) {
        try {
            Document doc = DocumentBuilderFactory.newInstance().newDocumentBuilder().parse(xdbxml);
            parse(doc.getDocumentElement());
        } catch (Exception e) {
            xdb.Trace.error("XdbConf", e);
            throw new RuntimeException(e);
        }
    }

    public static String getString(Element self, String name, String def) {
        String s = self.getAttribute(name);
        return s.isEmpty() ? def : s;
    }

    public static int getInt(Element self, String name, int def) {
        String s = self.getAttribute(name);
        return s.isEmpty() ? def : Integer.parseInt(s);
    }

    public static boolean getBoolean(Element self, String name, boolean def) {
        String s = self.getAttribute(name);
        return s.isEmpty() ? def : Boolean.parseBoolean(s);
    }

    private static void deprecatedAttribute(Element self, String name) {
        if (false == self.getAttribute(name).isEmpty()) {
            System.out.println("Deprecated attribute \"" + name + "\" in node \"" + self.getNodeName() + "\"");
        }
    }

    private void parse(Element self) throws Exception {

        corePoolSize = Integer.parseInt(self.getAttribute("corePoolSize"));
        procPoolSize = getInt(self, "procPoolSize", corePoolSize);
        schedPoolSize = getInt(self, "schedPoolSize", corePoolSize / 11 + 1);
        timeoutPeriod = getInt(self, "timeoutPeriod", 2000);

        deprecatedAttribute(self, "flushFatalTime");
        deprecatedAttribute(self, "flushPeriod");
        snapshotFatalTime = getInt(self, "snapshotFatalTime", 2000);
        marshalPeriod = getInt(self, "marshalPeriod", -1);
        marshalN = getInt(self, "marshalN", 1);
        checkpointPeriod = Integer.parseInt(self.getAttribute("checkpointPeriod"));
        allowCheckpointXXX = getBoolean(self, "allowCheckpointXXX", true);
        angelPeriod = Integer.parseInt(self.getAttribute("angelPeriod"));
        xdbVerify = getBoolean(self, "xdbVerify", false);
        isJson = getBoolean(self, "isJson", false);

        serialKey = getInt(self, "serialKey", 1024);
        localId = getInt(self, "localId", 0);
        defaultTableCache = self.getAttribute("defaultTableCache").trim();
        closeCache = getInt(self, "closeCache", 0);

        NodeList childnodes = self.getChildNodes();
        for (int i = 0; i < childnodes.getLength(); ++i) {
            Node node = childnodes.item(i);
            if (Node.ELEMENT_NODE != node.getNodeType()) {
                continue;
            }

            Element e = (Element) node;
            String nodename = e.getNodeName();
            if (nodename.equals("table") || nodename.equals("TableSysConf")) {
                TableConf conf = new TableConf(e);
                tableconf.put(conf.getName(), conf);
            } else if (nodename.equals("ProcedureConf")) {
                procedureConf = new ProcedureConf(e);
            } else if (nodename.equals("define")) {
                xgen.Define.getInstance().parse(e);
            }
        }
        // default
        if (null == this.procedureConf) {
            this.procedureConf = new ProcedureConf();
        }
    }

    public String getDefaultTableCache() {
        return defaultTableCache;
    }

    /**
     * xdb是否关闭缓存
     *
     * @return
     */
    public boolean closeCache() {
        return closeCache > 0;
    }

    public int getTimeoutPeriod() {
        return timeoutPeriod;
    }

    /**
     * 必须在启动前设置有效。
     *
     * @param timeoutPeriod
     */
    public synchronized void setTimeoutPeriod(int timeoutPeriod) {
        this.timeoutPeriod = timeoutPeriod;
    }

    public int getSerialKey() {
        return serialKey;
    }

    public long getSnapshotFatalTime() {
        return snapshotFatalTime;
    }

    /**
     * 当 snapshot 的时间超过此配置值时，记录 fatal 级别的日志。
     *
     * @param snapshotFatalTime
     */
    public synchronized void setSnapshotFatalTime(long snapshotFatalTime) {
        this.snapshotFatalTime = snapshotFatalTime;
    }

    public boolean isXdbVerify() {
        return xdbVerify;
    }

    boolean isAllowCheckpointXXX() {
        return allowCheckpointXXX;
    }

    public int getLocalId() {
        return localId;
    }

    public int getSchedPoolSize() {
        return schedPoolSize;
    }

    public int getAngelPeriod() {
        return angelPeriod;
    }

    public ProcedureConf getProcedureConf() {
        return procedureConf;
    }

    public int getCorePoolSize() {
        return corePoolSize;
    }

    public int getProcPoolSize() {
        return procPoolSize;
    }

    public final int getMarshalN() {
        return marshalN;
    }

    /**
     * flush锁之前执行并发 marshalN 的次数。默认为 1。
     * <b>
     * 警告: 配置小于 1 时，记录系列化在 marshal0 中完成，这将加大flush锁定的时间。
     * <b>
     * 运行时修改有效。
     *
     * @see xdb.Checkpoint
     */
    public synchronized void setMarshalN(int marshalN) {
        this.marshalN = marshalN;
    }

    public int getMarshalPeriod() {
        return marshalPeriod;
    }

    /**
     * 自动 marshal 的时间间隔。
     * 用于测试。运行时修改有效。
     * <=0 关闭自动 Marshal。
     */
    public synchronized void setMarshalPeriod(int marshalPeriod) {
        this.marshalPeriod = marshalPeriod;
    }

    public boolean isAutoMarshal() {
        return this.autoMarshal;
    }

    /**
     * for debug.
     */
    public synchronized void setAutoMarshal(boolean flush) {
        this.autoMarshal = flush;
    }

    public int getCheckpointPeriod() {
        return checkpointPeriod;
    }

    /**
     * 设置 checkpoint 定时间隔。
     * <p> 可以在运行时修改。修改的间隔在完成一次checkpoint以后生效。
     *
     * @param checkpointPeriod
     */
    public synchronized void setCheckpointPeriod(int checkpointPeriod) {
        if (checkpointPeriod < 0) {
            throw new IllegalArgumentException();
        }
        this.checkpointPeriod = checkpointPeriod;
    }

    public Map<String, TableConf> getTableConfs() {
        return tableconf;
    }

    public TableConf getTableConf(String name) {
        return tableconf.get(name);
    }

    public File getLibdbPath() {
        return libdbPath;
    }

    /**
     * 设置libdb库的路径。路径必须存在。
     */
    public synchronized void setLibdbPath(File libpath) {
        if (!libpath.isDirectory() || !libpath.exists()) {
            throw new XError("libpath : " + libpath + " (!isDirectory || !exists())");
        }
        this.libdbPath = libpath;
    }

//	/**
//	 * 设置uniqnameConf. add by cjc
//	 *
//	 */
//	public synchronized void setUniqnameConf(xdb.util.UniqNameConf uniq) {
//		this.uniqNameConf = uniq;
//	}

    public boolean isJson() {
        return isJson;
    }

    public void setJson(boolean isJson) {
        this.isJson = isJson;
    }

}
